//
//  WCBuildTarget.m
//  WabbitCode
//
//  Created by William Towe on 1/10/09.
//  Copyright 2009 Revolution Software. All rights reserved.
//

#import "WCBuildTarget.h"
#import "WCBuildConfiguration.h"
#import "NSDictionary+WCExtensions.h"
#import "NSImage+WCExtensions.h"
#import "WCBuildStep.h"
#import "WCTextFile.h"


#define kWCBTTargetModelKey @"targetModel"
#define kWCBTTargetOutputTypeKey @"targetOutputType"
#define kWCBTIsTargetActiveKey @"isTargetActive"
#define kWCBTBuildConfigurationsKey @"buildConfigurations"
#define kWCBTGetInfoWindowFrameKey @"getInfoWindowFrame"

NSString *kWCBTActiveBuildConfigurationDidChangeNotification = @"kWCBTActiveBuildConfigurationDidChangeNotification";
NSString *kWCBTBuildTargetNameDidChangeNotification = @"kWCBTBuildTargetNameDidChangeNotification";

@implementation WCBuildTarget
#pragma mark *** Subclass Overrides ***
- (void)dealloc {
	[_buildConfigurations release];
	[super dealloc];
}

- (void)setName:(NSString *)value {
	[super setName:value];
	[[NSNotificationCenter defaultCenter] postNotificationName:kWCBTBuildTargetNameDidChangeNotification object:self];
}

- (NSImage *)icon {
	if ([self isTargetActive])
		return [[NSImage imageNamed:@"Calculator16x16"] badgedImageWithImage:[NSImage imageNamed:@"ActiveBadge8x8"] originalSize:NSMakeSize(16.0, 16.0) badgeSize:NSMakeSize(8.0, 8.0)];
	return [NSImage imageNamed:@"Calculator16x16"];
}

- (NSArray *)acceptedDropTypes {
	static NSArray *types = nil;
	if (!types)
		types = [[NSArray alloc] initWithObjects:[WCBuildStep class], [WCTextFile class], nil];
	return types;
}

- (NSString *)nodeToolTip {
	return [NSString stringWithFormat:NSLocalizedString(@"Target Model: %@\nTarget Output Type: %@\nTotal Build Configurations: %u\nActive Build Configuration: %@",@"nodeToolTip for WCBuildTarget"), [self targetModelAsString], [self targetOutputTypeAsString], [self numberOfBuildConfigurations], [[self activeBuildConfiguration] name]];
}

- (BOOL)isDragable {
	return YES;
}

#pragma mark NSCoding Protocol
- (id)initWithCoder:(NSCoder *)coder {
	if (!(self = [super initWithCoder:coder]))
		return nil;
	
	_targetModel = [coder decodeIntegerForKey:kWCBTTargetModelKey];
	_targetOutputType = [coder decodeIntegerForKey:kWCBTTargetOutputTypeKey];
	_isTargetActive = [coder decodeBoolForKey:kWCBTIsTargetActiveKey];
	_buildConfigurations = [[coder decodeObjectForKey:kWCBTBuildConfigurationsKey] retain];
	_getInfoWindowFrame = NSRectFromString([coder decodeObjectForKey:kWCBTGetInfoWindowFrameKey]);
	
	return self;
}

- (void)encodeWithCoder:(NSCoder *)coder {
	[super encodeWithCoder:coder];
	[coder encodeInteger:[self targetModel] forKey:kWCBTTargetModelKey];
	[coder encodeInteger:[self targetOutputType] forKey:kWCBTTargetOutputTypeKey];
	[coder encodeBool:[self isTargetActive] forKey:kWCBTIsTargetActiveKey];
	[coder encodeObject:[self buildConfigurations] forKey:kWCBTBuildConfigurationsKey];
	[coder encodeObject:NSStringFromRect(_getInfoWindowFrame) forKey:kWCBTGetInfoWindowFrameKey];
}
#pragma mark NSCopying Protocol
- (id)copyWithZone:(NSZone *)zone {
	WCBuildTarget *copy = [super copyWithZone:zone];
	
	copy->_targetModel = _targetModel;
	copy->_targetOutputType = _targetOutputType;
	copy->_isTargetActive = _isTargetActive;
	copy->_buildConfigurations = [_buildConfigurations retain];
	
	return self;
}
#pragma mark NSMutableCopying Protocol
- (id)mutableCopyWithZone:(NSZone *)zone {
	WCBuildTarget *copy = [super mutableCopyWithZone:zone];
	
	copy->_targetModel = _targetModel;
	copy->_targetOutputType = _targetOutputType;
	copy->_isTargetActive = NO;
	
	for (WCBuildConfiguration *config in [self buildConfigurations])
		[[copy buildConfigurations] addObject:[[config mutableCopy] autorelease]];
	
	return copy;
}
#pragma mark *** Public Methods ***

#pragma mark Accessors
@synthesize targetModel=_targetModel, targetOutputType=_targetOutputType, isTargetActive=_isTargetActive,getInfoWindowFrame=_getInfoWindowFrame;

@dynamic buildTargetsGroup;
- (WCBuildTargetsGroup *)buildTargetsGroup {
	return (WCBuildTargetsGroup *)[self parentNode];
}

@dynamic numberOfBuildSteps;
- (NSUInteger)numberOfBuildSteps {
	return [[self childNodes] count];
}

@dynamic activeBuildConfiguration;
- (WCBuildConfiguration *)activeBuildConfiguration {
	for (WCBuildConfiguration *config in [self buildConfigurations])
		if ([config isConfigurationActive])
			return config;
	
	if ([[self buildConfigurations] count]) {
		[self setActiveBuildConfiguration:[[self buildConfigurations] firstObject]];
		return [self activeBuildConfiguration];
	}
	return nil;
}

- (void)setActiveBuildConfiguration:(WCBuildConfiguration *)value {
	for (WCBuildConfiguration *config in [self buildConfigurations])
		[config setIsConfigurationActive:NO];
	
	[value setIsConfigurationActive:YES];
	
	[[NSNotificationCenter defaultCenter] postNotificationName:kWCBTActiveBuildConfigurationDidChangeNotification object:self];
}

@dynamic numberOfBuildConfigurations;
- (NSUInteger)numberOfBuildConfigurations {
	return [[self buildConfigurations] count];
}

@dynamic targetModelAsString;
- (NSString *)targetModelAsString {
	switch ([self targetModel]) {
		case WCBTModelTI73:
			return NSLocalizedString(@"TI-73",@"TI-73");
		case WCBTModelTI82:
			return NSLocalizedString(@"TI-82",@"TI-82");
		case WCBTModelTI83:
			return NSLocalizedString(@"TI-83",@"TI-83");
		case WCBTModelTI83P84P:
			return NSLocalizedString(@"TI-83+",@"TI-83+");
		case WCBTModelTI85:
			return NSLocalizedString(@"TI-85",@"TI-85");
		case WCBTModelTI86:
			return NSLocalizedString(@"TI-86",@"TI-86");
		default:
			return NSLocalizedString(@"Unknown Model",@"Unknown Model");
	}
}

@dynamic targetOutputTypeAsString;
- (NSString *)targetOutputTypeAsString {
	switch ([self targetOutputType]) {
		case WCBTOutputTypeBinary:
			return NSLocalizedString(@"Binary",@"Binary");
		case WCBTOutputTypeProgram:
			return NSLocalizedString(@"Program",@"Program");
		case WCBTOutputTypeProgramIon:
			return NSLocalizedString(@"Ion",@"Ion");
		case WCBTOutputTypeProgramMirage:
			return NSLocalizedString(@"Mirage",@"Mirage");
		case WCBTOutputTypeProgramCrASH:
			return NSLocalizedString(@"CrASH",@"CrASH");
		case WCBTOutputTypeApplication:
			return NSLocalizedString(@"Application",@"Application");
		case WCBTOutputTypeStrings:
			return NSLocalizedString(@"Strings",@"Strings");
		default:
			return NSLocalizedString(@"Unknown Output Type",@"Unknown Output Type");
	}
}

@dynamic defaultNameForModelAndOutputType;
- (NSString *)defaultNameForModelAndOutputType {
	return [NSString stringWithFormat:@"%@%@", [self targetModelAsString], [self targetOutputTypeAsString]];
}

@dynamic outputFileExtension;
- (NSString *)outputFileExtension {
	switch ([self targetOutputType]) {
		case WCBTOutputTypeBinary:
			return @"bin";
		case WCBTOutputTypeProgramCrASH:
			return @"82p";
		case WCBTOutputTypeProgram:
			switch ([self targetModel]) {
				case WCBTModelTI73:
					return @"73p";
				case WCBTModelTI83:
					return @"83p";
				case WCBTModelTI83P84P:
					return @"8xp";
				case WCBTModelTI85:
					return @"85p";
				case WCBTModelTI86:
					return @"86p";
				default:
					@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Unrecognized model for output type program" userInfo:nil];
			}
			break;
		case WCBTOutputTypeProgramIon:
			switch ([self targetModel]) {
				case WCBTModelTI83:
					return @"83p";
				case WCBTModelTI83P84P:
					return @"8xp";
				default:
					@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Unrecognized model for output type program" userInfo:nil];
			}
			break;
		case WCBTOutputTypeProgramMirage:
			switch ([self targetModel]) {
				case WCBTModelTI83:
					return @"83p";
				case WCBTModelTI83P84P:
					return @"8xp";
				default:
					@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Unrecognized model for output type program" userInfo:nil];
			}
			break;
		case WCBTOutputTypeApplication:
			return @"8xk";
		case WCBTOutputTypeStrings:
			switch ([self targetModel]) {
				case WCBTModelTI85:
					return @"85s";
				case WCBTModelTI86:
					return @"86s";
				default:
					@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Unrecognized model for output type program" userInfo:nil];
			}
			break;
		default:
			@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Unrecognized target output type" userInfo:nil];
	}
}

@dynamic buildConfigurations;
- (NSMutableArray *)buildConfigurations {
	if (!_buildConfigurations) {
		_buildConfigurations = [[NSMutableArray alloc] init];
	}
	return _buildConfigurations;
}
#pragma mark Creation
+ (id)buildTargetWithModel:(WCBTModel)targetModel outputType:(WCBTOutputType)targetOutputType; {
	return [[[[self class] alloc] initWithModel:targetModel outputType:targetOutputType] autorelease];
}
+ (id)buildTargetWithModel:(WCBTModel)targetModel outputType:(WCBTOutputType)targetOutputType active:(BOOL)isTargetActive; {
	return [[[[self class] alloc] initWithModel:targetModel outputType:targetOutputType active:isTargetActive] autorelease];
}

- (id)initWithModel:(WCBTModel)targetModel outputType:(WCBTOutputType)targetOutputType; {
	return [self initWithModel:targetModel outputType:targetOutputType active:NO];
}
- (id)initWithModel:(WCBTModel)targetModel outputType:(WCBTOutputType)targetOutputType active:(BOOL)isTargetActive; {
	if (!(self = [super initWithNodeName:nil]))
		return nil;
	
	_targetModel = targetModel;
	_targetOutputType = targetOutputType;
	_isTargetActive = isTargetActive;
	_name = [[self defaultNameForModelAndOutputType] retain];
	
	[[self buildConfigurations] addObject:[WCBuildConfiguration buildConfigWithName:NSLocalizedString(@"Debug",@"Debug") forBuildTarget:self]];
	[[self buildConfigurations] addObject:[WCBuildConfiguration buildConfigWithName:NSLocalizedString(@"Release",@"Release") forBuildTarget:self]];
	
	(void)[self activeBuildConfiguration];
	
	return self;
}

@end
